package com.example.routeplansystem.algorithm.imp;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.example.routeplansystem.algorithm.Algorithm;
import com.example.routeplansystem.dao.EdgesDao;
import com.example.routeplansystem.dao.NodeDao;
import com.example.routeplansystem.dao.QuestionNodeDao;
import com.example.routeplansystem.entity.po.Edges;
import com.example.routeplansystem.entity.po.FinalSolution;
import com.example.routeplansystem.entity.po.NodePojo;
import com.example.routeplansystem.entity.po.QuestionNodePojo;
import com.example.routeplansystem.entity.po.Solution;
import com.example.routeplansystem.entity.po.VehicleMessage;
import com.example.routeplansystem.service.impl.WebSocketService;
import com.example.routeplansystem.utils.DistanceUtil;

/**
 * @author tanghaijun <tanghaijun@kuaishou.com>
 * Created on 2024-05-06
 */
@Component
public class ACOAlgorithm implements Algorithm {


    @Autowired
    private EdgesDao edgesDao;

    @Autowired
    private QuestionNodeDao questionNodeDao;

    @Autowired
    private NodeDao nodeDao;

    public List<Solution> getSolution(List<NodePojo> nodes, List<NodePojo> centerNodes, List<VehicleMessage> vehicles,
            FinalSolution finalSolution) {

        int min = Integer.MAX_VALUE;
        int[] bestRouter = new int[nodes.size() + 1];

        List<Solution> result = new ArrayList<>();

        int[][] distance = getDistance(nodes, finalSolution.getFinalSolutionId());
        //计算十次取最优
        for (int i = 0; i < 10; i++) {
            // 城市数量, 蚂蚁数量, 迭代次数, Alpha, Beta, Rho, Q, QType
            ACO aco = new ACO(nodes.size(), 10, 100, 1.0, 10.0, 0.5, 10, 0);
            aco.init(distance);
            aco.solve();
            if (aco.getBestLength() < min) {
                min = aco.getBestLength();
                bestRouter = Arrays.copyOf(aco.getBestTour(), aco.getBestTour().length);
            }
        }
        Solution solution = new Solution();
        solution.setFinalSolutionId(finalSolution.getFinalSolutionId());
        solution.setVehicleId(vehicles.get(0).getVehicleId());
        solution.setTotalDis(min);
        List<NodePojo> routeList = new ArrayList<>(nodes.size() + 1);
        Arrays.stream(bestRouter).forEach(i -> routeList.add(nodes.get(i)));
        solution.setRoute(JSON.toJSONString(routeList));
        result.add(solution);
        return result;
    }


    @Override
    public void checkMatrix(Integer questionId, Integer finalSolutionId) {
        List<QuestionNodePojo> questionNode = questionNodeDao.selectAllNode(questionId);
        List<Integer> nodeIds = questionNode.stream().map(QuestionNodePojo::getNodeId).collect(Collectors.toList());
        Map<Integer, NodePojo>
                nodeMap =
                nodeDao.selectNodeByIdList(nodeIds).stream().collect(Collectors.toMap(NodePojo::getNodeId, n -> n));
        List<NodePojo> nodes = questionNode.stream().map(n -> nodeMap.get(n.getNodeId())).collect(Collectors.toList());
        int n = nodes.size();
        List<Integer> list = nodes.stream().map(NodePojo::getNodeId).collect(Collectors.toList());
        List<Edges> edges = edgesDao.findNodes(list);
        HashMap<Integer, Integer> map = new HashMap<>(n);
        for (int i = 0; i < n; i++) {
            map.put(list.get(i), i);
        }
        int[][] matrix = new int[n][n];
        for (Edges node : edges) {
            int i = map.get(node.getFromNode());
            int j = map.get(node.getToNode());
            matrix[i][j] = node.getDistance();
        }
        int count = 0;
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                if (i == j) {
                    continue;
                }
                if (matrix[i][j] == 0) {
                    Integer distance = DistanceUtil.getDistance(nodes.get(i), nodes.get(j));
                    System.out.println("第" + (count++) + "条数据:" + list.get(i) + "-" + list.get(j) + ":" + distance);
                }
            }
            //发送进度
            WebSocketService.sendToUser(finalSolutionId + "|" + String.format("%.2f", i * 100.0 / matrix.length));

        }
        WebSocketService.onClose(finalSolutionId + "");
    }


    /**
     * 计算距离矩阵
     */
    public int[][] getDistance(List<NodePojo> nodes, Integer finalSolutionId) {
        // 获得矩阵
        int n = nodes.size();
        List<Integer> list = nodes.stream().map(NodePojo::getNodeId).collect(Collectors.toList());
        List<Edges> edges = edgesDao.findNodes(list);
        HashMap<Integer, Integer> map = new HashMap<>(n);
        for (int i = 0; i < n; i++) {
            map.put(list.get(i), i);
        }
        int[][] matrix = new int[n][n];
        for (Edges node : edges) {
            int i = map.get(node.getFromNode());
            int j = map.get(node.getToNode());
            matrix[i][j] = node.getDistance();
        }
        return matrix;
    }


}
